package org.os.framework.crypto.engine;

import java.math.BigInteger;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Random;

import org.bouncycastle.util.encoders.Hex;
import org.os.framework.crypto.model.Generator;
import org.os.framework.crypto.model.Keypair;

/**
 * @author 
 */
public class PaillierGenerator implements Generator {

    private final int modules = 512;
    private final int certainty = 64;
    private final BigInteger G = BigInteger.valueOf(2);
    
    @Override
    public Keypair generate() {
        BigInteger p = new BigInteger(modules / 2, certainty, new Random());
        BigInteger q = new BigInteger(modules / 2, certainty, new Random());
        BigInteger n = p.multiply(q);
        BigInteger n2 = n.multiply(n);
        BigInteger lambda = p.subtract(BigInteger.ONE)
                .multiply(q.subtract(BigInteger.ONE))
                .divide(p.subtract(BigInteger.ONE).gcd(q.subtract(BigInteger.ONE)));
        if (G.modPow(lambda, n2).subtract(BigInteger.ONE).divide(n).gcd(n).intValue() != 1) {
            throw new RuntimeException("Paillier密钥生成失败");
        }
        KeyPair kp = new KeyPair(
                new PublicKey() {
                    @Override
                    public String getAlgorithm() {
                        return "Paillier";
                    }

                    @Override
                    public String getFormat() {
                        return Hex.toHexString(getEncoded());
                    }

                    @Override
                    public byte[] getEncoded() {
                        return n.toByteArray();
                    }
                },
                new PrivateKey() {
                    @Override
                    public String getAlgorithm() {
                        return "Paillier";
                    }

                    @Override
                    public String getFormat() {
                        return Hex.toHexString(getEncoded());
                    }

                    @Override
                    public byte[] getEncoded() {
                        return lambda.toByteArray();
                    }
                });
        String prviateKey = kp.getPrivate().getFormat();
        String publicKey = kp.getPublic().getFormat();
        return Keypair.builder()
                .privateKey(prviateKey)
                .publicKey(publicKey)
                .build();
    }

}
